import random

from simpy import Environment

from x_pc.core import System
from x_pc.core.system_context import SysDataCtx
from x_pc.core.system_zb import SystemZB
from x_pc.core.transport.transport_403_agvby import AGVBY
from x_pc.model.model import TaskCtx, Task, check_fix_task, TaskStatus


class SystemAGV(System):
    # 仓库系统
    ware_house = None

    # 栈板系统
    system_zb: SystemZB = None
    sys_data: SysDataCtx = None

    # AGV列表
    agv_list: list[AGVBY] = []

    def __init__(self, env: Environment, name: str, system_zb: SystemZB, ware_house, sys_data: SysDataCtx):
        self.agv_list: list[AGVBY] = []
        #
        super().__init__(env, name)
        self.system_zb = system_zb
        self.ware_house = ware_house
        self.sys_data = sys_data
        #
        self.__init_agvby_list()

    def __init_agvby_list(self):
        """
        AGV搬运列表
        :return:
        """
        agvby_list = self.sys_data.get_data(AGVBY)
        for agvby in agvby_list:
            device = AGVBY(self.env, f'AGV搬运-{agvby[SysDataCtx.DEVICE_CODE_DB_KEY]}', agvby)
            if self.sys_data.is_valid_equ(device):
                self.agv_list.append(device)

    def __process_zb(self):
        """
        处理 栈板出口 任务
        :return:
        """
        while self.running:
            if not self.system_zb.zbcr:
                self.sys_data.logger.error(f'[PC] ZB ZBCR 栈板出入 无可用口 ')
                break
            # 当 栈板出入口 无任务时，等待
            if not self.system_zb.zbcr.store_list:
                yield self.env.timeout(1)
                continue
            # 当有任务时，选择
            task_ctx = self.system_zb.zbcr.store_list.pop(0)
            self.env.process(self.run_task(task_ctx))

    def __process_zb2(self):
        """
        处理 出入货口设备组
        """
        while self.running:
            if not self.system_zb.iqccr:
                self.sys_data.logger.error(f'[PC] ZB IQCCR 出入货口设备组 无可用口 ')
                break
            # 当 出入货口设备组 无任务时，等待
            if not self.system_zb.iqccr.store_list:
                yield self.env.timeout(1)
                continue
            # 当有任务时，选择
            task_ctx = self.system_zb.iqccr.store_list.pop(0)
            self.env.process(self.run_task(task_ctx))

    def __process_cm(self):
        """
        处理 拆模站台出口 任务
        :return:
        """
        while self.running:
            # 处理拆模站台任务
            if not self.task_list:
                yield self.env.timeout(1)
                continue
            task_ctx = self.task_list.pop(0)
            self.env.process(self.run_task(task_ctx))

    def run_task(self, task_ctx: TaskCtx):
        """
        AGV小车 执行搬运任务
        :param task_ctx:
        :return:
        """
        task = task_ctx.task
        process = task.process
        # 根据任务来选择 AGV小车(路线)
        agvby: AGVBY = self.choose_agv(task)
        if not agvby:
            self.sys_data.logger.warning(f'[PC] task: {task.task_id} 找不到 AGVBY设备')
            return

        process.begin_ts = self.env.now
        process.begin_pos = task_ctx.task.pre_process().end_pos
        process.set_origin_pack_type(task.pre_process().target_pack_type)
        process.set_origin_sku_num(task.pre_process().target_sku_num)
        process.operate_pack_type = process.ext_work_type.operate_pack_type
        process.operate_pack_num = 1
        #
        process.device_type = agvby.device_type()
        process.device_code = agvby.device_code()
        yield self.env.timeout(agvby.cost() / self.sys_data.time_cost_ratio)
        #
        process.end_pos = process.device_code
        process.end_ts = self.env.now
        #
        check_fix_task(task_ctx.task)
        #
        if TaskStatus.DONE != task.run_status:
            self.ware_house.distribute_task(task_ctx)

    def choose_agv(self, task: Task) -> AGVBY:
        """
        根据任务类别 判别走哪个线路
        :param task:
        :return:
        """
        # 根据任务获取 AGV搬运小车
        step = task.process.ext_step
        #
        available_agvs = [_ for _ in self.agv_list if _.id in step.ext_equid_list]
        if len(step.ext_equid_list) != len(available_agvs):
            self.sys_data.logger.warning(
                f'[PC] AGV设备数据异常 task: {task.task_id} route_step配置: id: {step.id}, equid_list: {step.ext_equid_list}; 有效agvs: {[_.id for _ in available_agvs]}')
        # 随机选择一个AGV
        return random.choice(available_agvs) if available_agvs else None

    def start(self):
        super().start()
        self.running = True
        # 栈板出入口 AGV任务
        self.env.process(self.__process_zb())
        # 栈板出入口 AGV任务2
        self.env.process(self.__process_zb2())
        # 拆模站台 任务
        self.env.process(self.__process_cm())
